﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Axe.Extensions;
using Axe.Collections;

namespace Axe.Data.Mapping
{
    public class SqlUpdate : SqlAction
    {
        public SqlUpdate(SqlConnection conn, bool close)
            : base(conn, close)
        {

        }

        public SqlUpdate(SqlConnection conn, bool close, SqlTransaction tran, bool commit, bool roolback)
            : base(conn, close,tran,commit,roolback)
        {
            
        }

        public bool Update<T>(Condition c,string property, object value)
        {
            Type objectType = typeof(T);
            MappingDescription descr = MappingDescriptionBuilder.GetDescription(objectType);

            return Template<bool>(
                delegate(SqlCommand cmd)
                {
                    string format = "UPDATE [{0}] SET {1}={2} WHERE {3}";
                    string fieldName = descr.MappedFields[property].MappedName;
                    string newValueParamName = String.Format("@{0}_NEW", fieldName);

                    string condText = RegisterCondition(cmd, c, descr);

                    cmd.Parameters.AddWithValue(newValueParamName, value);

                    string cmdText = String.Format(format, 
                        descr.Entity.MappedTableName,
                        fieldName,
                        newValueParamName,
                        condText);

                    cmd.CommandText = cmdText;
                },
                delegate(SqlCommand cmd)
                {
                    return cmd.ExecuteNonQuery() > 0;
                });
        }

        public bool Update<T>(T obj, bool diff) where T : class
        {
            Type objType = typeof(T);
            return Update(obj, objType, diff);            
        }

        public bool Update(object obj, Type objType, bool useDiffUpdate)
        {
            if (!objType.IsAssignableFrom(obj.GetType()))
                throw new ArgumentException(Res_Exceptions.IncomptabileDeclaredAndActualTypes);

            MappingDescription descr = MappingDescriptionBuilder.GetDescription(objType);
            if (descr.HasPrimaryKey == false)
                throw new InvalidOperationException(Res_Exceptions.MissingPK);

            object primaryKeyValue = Mapper.GetPrimaryKeyValue(obj, objType);

            return Template<bool>(
                // Prepare command to execute
                            delegate(SqlCommand cmd)
                            {
                                StringBuilder sb = new StringBuilder();
                                foreach (StringPair item in descr.UpdatedFields)
                                {
                                    PropertyDescription pDescr = descr.MappedFields[item.Left];
                                    object value = pDescr.GetValue(obj);
                                    string parameterName = "@" + pDescr.MappedName;
                                    cmd.Parameters.AddWithValue(parameterName, value);
                                    sb.AppendFormat("{0}=@{0},", pDescr.MappedName);
                                }
                                if (sb.Length > 0)
                                    sb.Remove(sb.Length - 1, 1);

                                string updateQuery = sb.ToString();

                                cmd.CommandText = String.Format("UPDATE [{0}] SET {1} WHERE {2}=@PrimaryKey",
                                    descr.Entity.MappedTableName,
                                    updateQuery, descr.PrimaryKey.MappedName);
                                cmd.Parameters.AddWithValue("@PrimaryKey", primaryKeyValue);

                            },

                            // Command execution
                            delegate(SqlCommand cmd)
                            {
                                bool result = cmd.ExecuteNonQuery() > 0;
                                return result;
                            });
        }

        
    }
}
